package pl.us.ij.plugins.segmentation;

import ij.IJ;
import ij.ImagePlus;
import ij.WindowManager;
import ij.gui.GUI;
import ij.plugin.frame.PlugInFrame;
import pl.us.ij.plugins.segmentation.commands.SharpenCommand;
import pl.us.ij.plugins.segmentation.commands.cvt.CvtSegmentationCommand;
import pl.us.ij.plugins.segmentation.commands.cvt.CvtShowEdgesCommand;
import pl.us.ij.plugins.segmentation.commands.edges.*;
import pl.us.ij.plugins.segmentation.commands.filters.AveragedFilterCommand;
import pl.us.ij.plugins.segmentation.commands.filters.GaussFilterCommand;
import pl.us.ij.plugins.segmentation.commands.filters.MedianFilterCommand;
import pl.us.ij.plugins.segmentation.commands.fire.FireSegmentationCommand;
import pl.us.ij.plugins.segmentation.commands.threshold.*;
import pl.us.ij.plugins.segmentation.commands.watershed.CombineSegmentationCommand;
import pl.us.ij.plugins.segmentation.commands.watershed.OCCOCommand;
import pl.us.ij.plugins.segmentation.commands.watershed.WatershedSegmentationCommand;
import pl.us.ij.plugins.segmentation.forms.ControlTemplateForm;
import pl.us.ij.plugins.segmentation.infrastructure.*;
import pl.us.ij.plugins.segmentation.localization.Localization;

import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import java.util.HashMap;


public class SegmentationPlugIn
        extends PlugInFrame
        implements ICommandInvokeHandler {

    private HashMap<String, Class> commands;

    private SwitchImageNotifier switchImageNotifier = new SwitchImageNotifier();

    private static boolean _terminate;

    private static SegmentationPlugIn _instance;

    public SegmentationPlugIn() {
        super(Localization.getString("segmentation.name"));

        if (_instance == null) {
            _terminate = false;
            _instance = this;
        } else {
            _instance.toFront();
            return;
        }

        initComponents();
        initCommands();
        registerCommands();

        switchImageNotifier.notifyListeners();
    }

    public static void addSwitchImageListener( SwitchImageListener l ) {
        _instance.switchImageNotifier.addSwitchImageListener(l);
    }

    public static void addSwitchImageListener(SwitchImageListener l, boolean notifyNow) {
        _instance.switchImageNotifier.addSwitchImageListener(l);
        if ( notifyNow ) {
            _instance.switchImageNotifier.notifyListeners();
        }
    }

    public static void removeSwitchImageListener( SwitchImageListener l ) {
        _instance.switchImageNotifier.removeSwitchImageListener(l);
    }
    
    @Override
    public void processWindowEvent(WindowEvent e) {
        super.processWindowEvent(e);
        if (e.getID() == WindowEvent.WINDOW_CLOSING) {
            _instance = null;
        }
    }

    public void invoke(String commandName) {
        this.invoke(commandName, null);
    }

    public void invoke(String commandName, CommandParams params) {
        Trace.log(commandName);

        ImagePlus image = IJ.getImage();

        if (image == null) {
            IJ.beep();
            IJ.showStatus(Localization.getString("segmentation.warning.noImage"));
            return;
        }

        if (!image.lock()) {
            return;
        }

        if (commandName == null || commandName.isEmpty()) {
            IJ.beep();
            IJ.showMessage(Localization.getString("message.error"), Localization.getString("segmentation.unknownCommand"));
            image.unlock();
            return;
        }

        ICommand command = null;
        try {
            command = lookupCommandBy(commandName);
        } catch (IllegalAccessException e) {
            IJ.showMessage(Localization.getString("message.error"), "IllegalAccessException: " + commandName);
        } catch (InstantiationException e) {
            IJ.showMessage(Localization.getString("message.error"), "InstantiationException: " + commandName);
        }

        if (command == null) {
            IJ.beep();
            IJ.showMessage(Localization.getString("message.error"), Localization.getString("segmentation.notRegistredCommand"));
            image.unlock();
            return;
        }

        command.setImage(image);
        command.setParams(params);

        new CommandRunner(image, params, command);
    }

    private void initComponents() {
        addKeyListener(IJ.getInstance());

        add(ControlTemplateForm.getMainPanel(this));
        this.setSize(790, 500);
        GUI.center(this);
        setVisible(true);
    }

    private void initCommands() {
        this.commands = new HashMap<String, Class>();
    }

    private void registerCommands() {
        registerCommand(SharpenCommand.class);
        registerCommand(GaussFilterCommand.class);
        registerCommand(AveragedFilterCommand.class);
        registerCommand(MedianFilterCommand.class);
        registerCommand(TwoLevelThresholdCommand.class);
        registerCommand(OneLevelThresholdCommand.class);
        registerCommand(IterativeHistogramThresholdCommand.class);
        registerCommand(HistogramThresholdCommand.class);
        registerCommand(GradientThresholdCommand.class);
        registerCommand(DetectEdgesCommand.class);
        registerCommand(ErosionCommand.class);
        registerCommand(DilatationCommand.class);
        registerCommand(SkeletonCommand.class);
        registerCommand(BranchCuttingCommand.class);
        registerCommand(OCCOCommand.class);
        registerCommand(WatershedSegmentationCommand.class);
        registerCommand(CombineSegmentationCommand.class);
        registerCommand(CvtSegmentationCommand.class);
        registerCommand(CvtShowEdgesCommand.class);
        registerCommand(FireSegmentationCommand.class);
        registerCommand(ShowPixelValuesCommand.class);
    }

    private void registerCommand(Class aClass) {
        Trace.log(aClass.getName());

        this.commands.put(aClass.getSimpleName(), aClass);
    }

    private ICommand lookupCommandBy(String label) throws IllegalAccessException, InstantiationException {

        Class commandClass = commands.get(label);

        if (commandClass == null)
            return null;

        return (ICommand) commandClass.newInstance();
    }

    @Override
    public void windowActivated(WindowEvent e) {
        super.windowActivated(e);

        ImagePlus imp = WindowManager.getCurrentImage();

        if (imp != null) {
            return;
        }
    }

    @Override
    public void focusGained(FocusEvent e) {
        super.focusGained(e);
    }

    @Override
    public void close() {
        super.close();

        _instance = null;
        _terminate = true;

        synchronized (this) {
            notify();
        }
    }

    @Override
    public void run(String arg) {
        while (!_terminate) {
            synchronized (this) {
                try {
                    wait();
                }
                catch (InterruptedException e) {
                }
            }
        }
    }

}








